﻿/* ----------------------------------------------------------------------------
 * Copyright (c) Rob Ashton 2009
 *
 * This source code is subject to terms and conditions of the Microsoft Public 
 * License (Ms-PL).
 * 
 * By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * To request a copy of the Microsoft Public License, send a request by email
 * to license@mvcex.com
 *
 * This notice must not be removed from this software.
 * ---------------------------------------------------------------------------- */


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using MvcEx.Configuration;

namespace MvcEx.Theming
{
    /// <summary>
    /// Utility class for performing file resolution within the current MvcExApplication context
    /// </summary>
    public static class MvcExThemePathResolver
    {
        /// <summary>
        /// Ensures that a path is not relative and is application aware
        /// </summary>
        /// <param name="relativePath"></param>
        /// <returns></returns>
        public static String TidyLinkedResourcePath(HttpContextBase context, String relativePath)
        {
            // First off, trim any trailing slashes that might be left over
            relativePath = relativePath.TrimStart('/', '\\');

            // And replace any back slashes with forward slashes because it's what everyone is used to
            relativePath = relativePath.Replace('\\', '/');

            if (context.Request.ApplicationPath == "/")
            {
                // Virtual directory is /
                return String.Format("/{0}", relativePath);
            }
            else
            {
                // Virtual directory is /something
                return string.Format("{0}/{1}", context.Request.ApplicationPath, relativePath);
            }
        }

        /// <summary>
        /// Resolves any file within the 'themes' directory to use an override specified by the current theme if available
        /// </summary>
        /// <param name="currentTheme">The name of the current theme</param>
        /// <param name="themeDirectoryName">The name of the top level directory containing all the themes</param>
        /// <param name="context">The HttpContext used to perform path resolution</param>
        /// <param name="themeFilePath">The original file we'll override if the theme wants to</param>
        /// <returns></returns>
        public static String ResolveThemeFileLocation(
            String currentTheme, 
            String themeDirectoryName , 
            HttpContextBase context, 
            String themeFilePath)
        {
            // We only allow overrides if the original file came from the themes directory
            if (!themeFilePath.Contains(themeDirectoryName)) { return themeFilePath; }

            // Get us some full rooted paths
            String fallbackThemePath = (!context.Request.ApplicationPath.Equals("/") && themeFilePath.StartsWith(context.Request.ApplicationPath) ? themeFilePath.Substring(context.Request.ApplicationPath.Length) : themeFilePath.TrimStart('~'));
            String fullThemeFilePath = CalculateFullThemeFilePath(context, themeFilePath);
            String fullThemePath = CalculateFullThemePath(context);

            // And the filename we're looking for
            String themeFilename = Path.GetFileName(themeFilePath);

            DirectoryInfo themeFileDirectory = new DirectoryInfo(fullThemeFilePath);
            DirectoryInfo baseDirectory = new DirectoryInfo(fullThemeFilePath);
            DirectoryInfo themesDirectory = new DirectoryInfo(fullThemePath);

            // We're looking for the top level of the actual theme directory
            while (baseDirectory.Parent != null && baseDirectory.Parent.FullName != themesDirectory.FullName)
            {
                baseDirectory = baseDirectory.Parent;
            }
            
            // This is a fun edge case where the file IS in A themes directory
            // But it's not *the* themes directory. I can see some muppets doing this..
            if (baseDirectory.Parent == null) { return fallbackThemePath; }

            // Becuase this allows us to work out the path to the file relative
            // to the theme directory and thus search the current theme for the file
            String relativeFileDirectory = themeFileDirectory.FullName.Replace(baseDirectory.FullName, "");

            // Path.Combine will not work with these trailing slashes, so get rid of them
            relativeFileDirectory = relativeFileDirectory.Trim('\\');

            // Search the active theme for this file
            String relativeFileLocation = Path.Combine(relativeFileDirectory, themeFilename);
            String activeThemeDirectory = Path.Combine(fullThemePath, currentTheme);
            String fullSearchPath = Path.Combine(activeThemeDirectory, relativeFileLocation);

            // If we found it, them hurrah - return it
            if (File.Exists(fullSearchPath)) 
            {
                return CreateRelativePath(context, fullSearchPath);
            }

            // Else, fallback to the explicitly requested file
            return fallbackThemePath;
        }

        private static string CreateRelativePath(HttpContextBase context, String fullSearchPath)
        {
            // Okay, now we really don't want this path rooted
            // So we'll chomp off the root
            String relativeRoot = context.Server.MapPath("~/");

            // Get the standardised path
            String fullRoot = new DirectoryInfo(relativeRoot).FullName;

            // And trim it off from the search path
            return fullSearchPath.Replace(fullRoot, "");
        }

        private static String CalculateFullThemePath(HttpContextBase context)
        {
            return context.Server.MapPath(Path.Combine("~/", MvcExConstants.THEME_DIRECTORY));
        }

        private static String CalculateFullThemeFilePath(HttpContextBase context, String masterPath)
        {
            return context.Server.MapPath(Path.GetDirectoryName(masterPath));
        }
    }
}
